package com.test.base.netty_upgrade.selfprotocol.protocol;

import com.test.base.netty_upgrade.selfprotocol.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * description 自定义编解码器件
 * 自定义的编码器是实现自定义protocol的基本方式
 * @author zken
 * @CreateDate 2024/11/21 20:04:42
 */
/*
我们可以注意到
ByteToMessageCodec是不可以被多线程给共享的（netty的文档中注释）
所以，如果想要单对象多个eventGroup进行复用
需要使用支持sharable的父类进行实现接口
 */
@Slf4j
public class MessageCodec extends ByteToMessageCodec<Message> {
    /**
     * description 自定义编码方法
     *
     * @param ctx pipeLine上下文
     * @author zken
     * @CreateDate 2024/11/21 20:04:51
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
        // 1.写入魔数4个字节
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2.写入版本，1个字节
        out.writeByte(1);
        // 3.写入序列化方式，如果是jdk则为0，如果是json则为1
        out.writeByte(0);
        // 4.字节指令的类型，1个字节
        out.writeByte(msg.getMessageType());
        // 5.写入sequenceId--4个字节，为了支持双工通信
        out.writeInt(msg.getSequenceId());
        // 加一个没有意义的1字节，让buf的长度满足4的倍数
        out.writeByte(0xff);
        // 6.获取内容的字节数组
        // 字节接受对象
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        // 对象接受对象
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        byte[] bytes = bos.toByteArray();
        // 7.写入讯息的字节数组的长度
        out.writeInt(bytes.length);
        // 8.写入字节的序列化字节流
        out.writeBytes(bytes);
    }

    /**
     * description
     *
     * @param in  输入buf流对象
     * @param out 输出的内容
     * @author zken
     * @CreateDate 2024/11/21 20:50:13
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 根据in种放入的内容来进行解析
        int magicNumber = in.readInt();
        int versionNumber = in.readByte();
        int serialNumber = in.readByte();
        int messageType = in.readByte();
        int sequenceId = in.readInt();
        // 读取占位符
        int placeHolder = in.readByte();
        // 读取数据长度和数据内容
        int bytesLength = in.readInt();
        // 根据长度读取body字节流的内容
        byte[] bytes = new byte[bytesLength];
        in.readBytes(bytes, 0, bytesLength);
        // 转化为实例化对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Message msg = (Message) ois.readObject();
        // 设置此次消息的类型
        msg.setMessageType(messageType);
        log.debug("magicNumber:{},versionNumber:{},serialNumber:{},messageType:{},sequenceId:{},placeHolder:{},bytesLength:{}",
                magicNumber,
                versionNumber,
                serialNumber,
                messageType,
                sequenceId,
                placeHolder,
                bytesLength);
        log.debug("message:{}", msg);
        // 将out种加入msg
        out.add(msg);
    }
}
